#include <iostream>
#include <mutex>
#include <cstdlib>
#include <thread>
#include <algorithm>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include "Server.h"
#include "Client.h"
#include "md5.h"
#include <sstream>
#include <ntp.h>
#include <string/ring_arrays.h>
#include <cmath>
#include "log_err.h"
#include "sign_kernel.h"
#include "transfer/sw_p2p_transfer.h"

using namespace std;

const int port = 8081;
const char *ip = "192.168.43.149";


class ServerCallback : public Server::ServerListener {
public:
    /* 1. 多进程/多线程 处理程序 */
    void callback(Server* server, Server::Client* client) {
        char IP_BUF[255];
        inet_ntop(AF_INET, (void*)&client->addr.sin_addr.s_addr, IP_BUF, client->addrlen);
        std::cout << "client ip:" << IP_BUF << " port:" << ntohs(client->addr.sin_port) << 
            " pid:" << getpid() << " clifd:" << client->fd << std::endl;
        std::string buffer = "Hello Client ";
        char data[255] = {0};
        buffer += std::to_string(getpid());
        server->Write(client->fd, buffer.c_str(), buffer.size());
        std::cout << ">>>>>>>>" << getpid() << std::endl;

        while (true) {
            int len = server->Read(client->fd, data, sizeof(data));
            if (len == 0) {
                return; /* 断开连接 返回 */
            }
            printf("%p recv:buffer = %s\r\n", &client->addr, data);
            // for (int k = 0; k < len; k++) {
            //     data[k] = toupper(data[k]);
            // }
            // server->Write(client->fd, data, len);
        }
    }
    /* 2. select 客户端建立连接后执行 */
    bool connect(Server* server, Server::Client* client)
    {
        char IP_BUF[255];
        inet_ntop(AF_INET, (void*)&client->addr.sin_addr.s_addr, IP_BUF, client->addrlen);
        std::cout << "client ip:" << IP_BUF << " port:" << ntohs(client->addr.sin_port) << 
            " pid:" << getpid() << " clifd:" << client->fd << std::endl;
        std::string buffer = "Hello Client ";
        buffer += std::to_string(getpid());
        server->Write(client->fd, buffer.c_str(), buffer.size());
        return true;/* 不断开连接 */
    }
    /* 3. select 接收到客户端发送的数据执行 */
    bool recv(Server* server, Server::Client* client, char* buffer, int len)
    {
        if ((buffer == nullptr) || (len <= 0)) false;
        printf("%p recv:buffer = %s\r\n", &client->addr, buffer);
        for (int k = 0; k < len; k++) {
            buffer[k] = toupper(buffer[k]);
        }
        server->Write(client->fd, buffer, len);
        return true;/* 不断开连接 */
    }
};

class HttpCallback : public Server::ServerListener {
private:
    char buffer[1024];
public:
    /* 1. 多进程/多线程 处理程序 */
    void callback(Server* server, Server::Client* client) {
        char IP_BUF[255];
        inet_ntop(AF_INET, (void*)&client->addr.sin_addr.s_addr, IP_BUF, client->addrlen);
        std::cout << "client ip:" << IP_BUF << " port:" << ntohs(client->addr.sin_port) << 
            " pid:" << getpid() << " clifd:" << client->fd << std::endl;
        server->Read(client->fd, buffer, sizeof(buffer));
        std::cout << buffer << std::endl;
        MD5 m5(buffer);
        cout << "md5:" << m5.toStr() <<endl;
        std::string sendinfo = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n";
        std::string index;
        getFileInfo("index.html", index);
        sendinfo += index;
        server->Write(client->fd, sendinfo.c_str(), sendinfo.size());
    };
    /* 2. select 客户端建立连接后执行 */
    bool connect(Server* server, Server::Client* client) {
        char IP_BUF[255];
        inet_ntop(AF_INET, (void*)&client->addr.sin_addr.s_addr, IP_BUF, client->addrlen);
        std::cout << "client ip:" << IP_BUF << " port:" << ntohs(client->addr.sin_port) << 
            " pid:" << getpid() << " clifd:" << client->fd << std::endl;
        return true;/* 不断开连接 */
    };
    /* 3. select 接收到客户端发送的数据执行 */
    bool recv(Server* server, Server::Client* client, char* buffer, int len) {
        std::cout << buffer << std::endl;
        MD5 m5(std::string(buffer, len));
        cout << "md5:" << m5.toStr() <<endl;
        std::string sendinfo = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n";
        std::string index;
        getFileInfo("index.html", index);
        sendinfo += index;
        server->Write(client->fd, sendinfo.c_str(), sendinfo.size());
        return false;/* 断开连接 */
    };
};

int server()
{
    MD5 m5("hello");
    cout << "md5:" << m5.toStr() <<endl;
    Server::ServerListener *listener = new ServerCallback();
    Server server(port, ip, 50);
    server.setListener(listener);
    server.startListen(Server::ServerEnum::FORK);
    delete listener;
    return 0;
}

void client(const char * ip)
{
    uint8_t buf[1024];
    Client *client = new Client(5555, ip);
    struct ring_arrays *array = ring_arrays_create_of_size(65535);
    client->Connect();
    while (true) {
        int len = client->Read(buf, sizeof(buf));
        if (len == 0) break;
        ring_arrays_add_buffer(array, buf, len);
        sw_p2p_analyse_comm_buffer(array);
    }
    ring_arrays_destroy(array);
}

void ntp(int argc, char const *argv[], const char* host)
{
    char buffer[NTP_BUFSIZE] = { 0 };
    struct timeval recvtv, tv;
    size_t nbytes = NTP_BUFSIZE;
    double offset = 0;
    Client client(NTP_PORT, host, SOCK_DGRAM);

    SIGNWAY_LOGI("host = %s", host);

    client.Connect();
    
    if (get_ntp_packet(buffer, &nbytes) != 0) {
        fprintf(stderr, "construct ntp request error \n");
        exit(-1);
    }
    client.Send(buffer, nbytes);
    SIGNWAY_LOGI("write ok");

    bzero(buffer, sizeof(buffer));
    nbytes = client.Recv(buffer, NTP_BUFSIZE);
    SIGNWAY_LOGI("read ok");

    gettimeofday(&recvtv, NULL);
    offset = get_offset((struct ntphdr *) buffer, &recvtv);
    std::cout << "offset:" << offset << std::endl;

    gettimeofday(&tv, NULL);
    tv.tv_sec += (int) offset;
    tv.tv_usec += (offset - (int)offset) * pow(10, 6);

    std::cout << "tv_sec:" << (int) offset << std::endl;
    std::cout << "tv_usec:" << (__suseconds_t) ((offset - (int)offset) * pow(10, 6)) << std::endl;

    if (settimeofday(&tv, NULL) != 0) {
        perror("settimeofday error");
        exit(-1);
    }
    SIGNWAY_LOGI("%s \n", ctime((time_t *) &tv.tv_sec));

    return;
}



int main(int argc, char const *argv[])
{
    if (argc < 2) {
        perror("Usage: ./main [socket ntp]");
        exit(1);
    }

    if (strcmp(argv[1], "server") == 0) {
        server();
    }

    if (strcmp(argv[1], "ntp") == 0) {
        ntp(argc, argv, Socket::GetHostByName(NTP_SERVER_NAME));
    }

    if (strcmp(argv[1], "client") == 0) {
        if (argc < 2) {
            client("127.0.0.1");
        } else {
            client(argv[2]);
        }
    }

    return 0;
}

